package ccm.communicate.serial;

import ccm.common.CCMConfigManager;
import ccm.common.ReceiveDataEvent;
import ccm.common.StatusChangeEvent;
import ccm.communicate.common.Communicate;
import ccm.communicate.common.NotStartException;
import ccm.communicate.common.SendFailedException;
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;

import javax.swing.*;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Serial extends Communicate implements SerialPortDataListener
{
    /**
     * 保存串口号
     */
    protected SerialPort serialPort;
    /**
     * 默认波特率
     */
    protected int baudRate=460800;
    /**
     * 默认数据位
     */
    protected int dataBits=8;
    /**
     * 默认停止位
     */
    protected int stopBits=SerialPort.ONE_STOP_BIT;
    /**
     * 默认校验位
     */
    protected int parity=SerialPort.NO_PARITY;

    boolean dtr;
    boolean rts;

    public Serial()
    {
        super();
        rts=false;
        dtr=false;
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(()->
        {
            if(serialPort!=null&&serialPort.getLastErrorCode()!=0)
            {
                stop();
            }
        },0,1,TimeUnit.SECONDS);
    }

    /**
     * 启动串口
     */
    public void start()
    {
        if(serialPort!=null&&!serialPort.isOpen())
        {
            try
            {
                updateDtrRts();
                serialPort.openPort();
                updateDtrRts();
                serialPort.setComPortParameters(baudRate,dataBits,stopBits,parity);
                updateDtrRts();
                callStatusChangeListener(StatusChangeEvent.START);
                updateDtrRts();
            }catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    private void updateDtrRts()
    {
        if(serialPort==null)
        {
            return;
        }
        if(dtr)
        {
            if(!serialPort.getDTR()) serialPort.setDTR();
        }
        else
        {
            if(serialPort.getDTR()) serialPort.clearDTR();
        }
        if(rts)
        {
            if(!serialPort.getRTS()) serialPort.setRTS();
        }
        else
        {
            if(serialPort.getRTS()) serialPort.clearRTS();
        }
    }

    /**
     * 停止串口
     */
    public void stop()
    {
        if(serialPort!=null&&serialPort.isOpen())
        {
            serialPort.closePort();
            callStatusChangeListener(StatusChangeEvent.STOP);
        }
    }

    public boolean isOpen()
    {
        return serialPort!=null&&serialPort.isOpen();
    }

    @Override
    synchronized public void receiveData(ReceiveDataEvent event) throws NotStartException, SendFailedException
    {
        if(serialPort==null||!serialPort.isOpen())
        {
            throw new NotStartException();
        }
        updateDtrRts();
        serialPort.writeBytes(event.getData(),event.getData().length);
    }

    /**
     * 获取串口号
     *
     * @return 串口号
     */
    public SerialPort getSerialPort()
    {
        return serialPort;
    }

    /**
     * 设置串口号
     *
     * @param serialPort 串口号
     */
    public void setSerialPort(SerialPort serialPort)
    {
        stop();
        this.serialPort=serialPort;
        if(this.serialPort!=null)
        {
            this.serialPort.addDataListener(this);
            final String confPrefix="ccm/communicate/serial/Serial/"+this.serialPort.getSystemPortName();
            setBaudRate(CCMConfigManager.setIntegerIfAbsent(confPrefix+"/baudRate",460800));
            setDataBits(CCMConfigManager.setIntegerIfAbsent(confPrefix+"/dataBits",8));
            setStopBits(CCMConfigManager.setIntegerIfAbsent(confPrefix+"/stopBits",SerialPort.ONE_STOP_BIT));
            setParity(CCMConfigManager.setIntegerIfAbsent(confPrefix+"/parity",SerialPort.NO_PARITY));
        }
    }

    /**
     * 获取波特率
     *
     * @return 波特率
     */
    public int getBaudRate()
    {
        return baudRate;
    }

    /**
     * 设置波特率
     *
     * @param baudRate 波特率
     */
    public void setBaudRate(int baudRate)
    {
        this.baudRate=baudRate;
        if(serialPort!=null)
        {
            serialPort.setBaudRate(this.baudRate);
            CCMConfigManager.setInteger("ccm/communicate/serial/Serial/"+this.serialPort.getSystemPortName()+"/baudRate",this.baudRate);
        }
    }

    /**
     * 获取数据位
     *
     * @return 数据位
     */
    public int getDataBits()
    {
        return dataBits;
    }

    /**
     * 设置数据位
     *
     * @param dataBits 数据位
     */
    public void setDataBits(int dataBits)
    {
        this.dataBits=dataBits;
        if(serialPort!=null)
        {
            serialPort.setNumDataBits(this.dataBits);
            CCMConfigManager.setInteger("ccm/communicate/serial/Serial/"+this.serialPort.getSystemPortName()+"/dataBits",this.dataBits);

        }
    }

    /**
     * 获取停止位
     *
     * @return 停止位
     */
    public int getStopBits()
    {
        return stopBits;
    }

    /**
     * 设置停止位
     *
     * @param stopBits 停止位
     */
    public void setStopBits(int stopBits)
    {
        this.stopBits=stopBits;
        if(serialPort!=null)
        {
            serialPort.setNumStopBits(this.stopBits);
            CCMConfigManager.setInteger("ccm/communicate/serial/Serial/"+this.serialPort.getSystemPortName()+"/stopBits",this.stopBits);
        }
    }

    /**
     * 获取校验位
     *
     * @return 校验位
     */
    public int getParity()
    {
        return parity;
    }

    /**
     * 设置校验位
     *
     * @param parity 校验
     */
    public void setParity(int parity)
    {
        this.parity=parity;
        if(serialPort!=null)
        {
            serialPort.setParity(parity);
            CCMConfigManager.setInteger("ccm/communicate/serial/Serial/"+this.serialPort.getSystemPortName()+"/parity",this.parity);
        }
    }

    public boolean isDtr()
    {
        return dtr;
    }

    public void setDtr(boolean dtr)
    {
        this.dtr=dtr;
        updateDtrRts();
    }

    public void setDtr(int dtr)
    {
        setDtr(dtr!=0);
    }

    public boolean isRts()
    {
        return rts;
    }

    public void setRts(boolean rts)
    {
        this.rts=rts;
        updateDtrRts();
    }

    public void setRts(int rts)
    {
        setRts(rts!=0);
    }

    /**
     * SerialPortDataListener实现
     */
    public int getListeningEvents()
    {
        return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
    }

    /**
     * SerialPortDataListener实现
     * 当串口接收到数据时触发
     */
    public void serialEvent(SerialPortEvent event)
    {
        if(event.getEventType()==SerialPort.LISTENING_EVENT_DATA_AVAILABLE)
        {
            byte[] newData=new byte[serialPort.bytesAvailable()];
            serialPort.readBytes(newData,newData.length);
            callReceiveDataListener(new ReceiveDataEvent(Serial.this,newData));
        }
    }

    /**
     * 获取配置用的面板
     *
     * @return 配置用的面板
     */
    public JPanel getConfigJPanel()
    {
        return new SerialConfigJPanel(this);
    }
}
